package com.gitee.huanminabc.jcommon.reflect;


import com.gitee.huanminabc.jcommon.base.Assert;
import com.gitee.huanminabc.jcommon.container.ArrayUtil;
import com.gitee.huanminabc.jcommon.exception.CommonException;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class BeanCopyUtil {

    //List to List
    public static <T> List<T> copyList(List<?> source, Class<T> targetClass, String... ignoreProperties) {
        if (source == null || source.isEmpty()) {
            return new ArrayList<>();
        }
        List<T> targetList = new ArrayList<>();
        for (Object item : source) {
            T t = ClassUtil.newInstance(targetClass);
            if (t == null) {
                throw new CommonException("目标类 " + targetClass.getName() + " 无法实例化,请检查是否有无参构造函数");
            }
            copy(item, t, ignoreProperties);
            targetList.add(t);
        }
        return targetList;
    }


    /**
     * @param source           源文件的
     * @param target           目标文件
     * @param ignoreProperties 忽略的属性
     * @return
     * @throws Exception
     */
    // 该方法实现对Customer对象的拷贝操作
    public static <T, O> void copy(T source, O target, String... ignoreProperties) {
        try {
            Class<?> classType = source.getClass();
            Class<?> targetClass = target.getClass();
            //获得对象的所有成员变量
            Field[] fields = classType.getDeclaredFields();
            for (Field field : fields) {
                //跳过静态属性和final属性
                if (skipField(field)) continue;
                //获取成员变量的名字
                String name = field.getName();//获取成员变量的名字，此处为id，name,age
                //排除ignoreProperties
                if (ignoreField(ignoreProperties, name)) continue;

                //获取get和set方法的名字
                String getMethodName = MethodUtil.getMethodCapitalize(field);
                String setMethodName = MethodUtil.fieldToSetMethod(field.getName());
                //获取方法对象
                try {
                    Method getMethod = classType.getMethod(getMethodName);
                    Method setMethod = targetClass.getMethod(setMethodName, field.getType());
                    //调用get方法获取旧的对象的值
                    Object value = getMethod.invoke(source);
                    //调用set方法将这个值复制到新的对象中去
                    setMethod.invoke(target, value);
                } catch (NoSuchMethodException ignored) {
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new CommonException(e);
        }

    }


    /**
     * 拷贝对象,忽略source是null值
     *
     * @param source
     * @param target
     * @param <T>
     */
    public static <T> void copyIgnoreNull(T source, T target, String... ignoreProperties) {
        Assert.notNull(source, "source 不能为空");
        Assert.notNull(target, "target 不能为空");
        String[] nullPropertyNames = getNullPropertyNames(source);
        String[] allIgnoreProperties = ArrayUtil.addAll(nullPropertyNames, ignoreProperties);
        copy(source, target, allIgnoreProperties);
    }


    //深拷贝list
    public static <T> List<T> deepCopyList(List<T> list) {
        List<T> newList = new ArrayList<>();
        for (T item : list) {
            newList.add(deepCopy(item));
        }
        return newList;
    }

    public static <T> T copy(T source) {
        try {
            Class<?> classType = source.getClass();
            Object objectCopy = ClassUtil.newInstance(source.getClass());
            //获得对象的所有成员变量
            Field[] fields = classType.getDeclaredFields();
            for (Field field : fields) {
                //跳过静态属性和final属性
                if (skipField(field)) continue;
                //获取get和set方法的名字
                String getMethodName = MethodUtil.getMethodCapitalize(field);
                String setMethodName = MethodUtil.fieldToSetMethod(field.getName());
                //获取方法对象
                try {
                    Method getMethod = classType.getMethod(getMethodName);
                    Method setMethod = classType.getMethod(setMethodName, field.getType());
                    //调用get方法获取旧的对象的值
                    Object value = getMethod.invoke(source, new Object[]{});
                    //调用set方法将这个值复制到新的对象中去
                    setMethod.invoke(objectCopy, new Object[]{value});
                } catch (NoSuchMethodException ignored) {
                }
            }
            return (T) objectCopy;
        } catch ( IllegalAccessException | InvocationTargetException e) {
            throw new CommonException(e);
        }
    }

    //深拷贝(序列化方式)
    public static <T> T deepCopy(T obj) {
        if (obj instanceof Serializable) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(obj);
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                return (T) ois.readObject();
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new RuntimeException("该对象不支持序列化,无法进行深拷贝,请实现Serializable接口");
        }
    }


    //属性拷贝
    public static <T, O> T copyField(T source, O target, String... ignoreProperties) {
        try {
            Class<?> classType = source.getClass();
            Class<?> targetClass = target.getClass();
            //获得对象的所有成员变量
            Field[] fields = classType.getDeclaredFields();
            for (Field field : fields) {
                //获取成员变量的名字，此处为id，name,age
                //跳过静态属性和final属性
                if (skipField(field)) continue;

                //获取成员变量的名字
                String name = field.getName();
                if (ignoreField(ignoreProperties, name)) continue;
                try {
                    //获取target的属性
                    Field targetField = targetClass.getDeclaredField(name);
                    ReflectUtil.setAccessible(field, targetField);
                    targetField.set(target, field.get(source));
                } catch (NoSuchFieldException ignored) {
                }
            }
            return (T) target;
        } catch (IllegalAccessException e) {
            throw new CommonException(e);
        }


    }

    public static boolean ignoreField(String[] ignoreProperties, String name) {
        //排除ignoreProperties
        if (ignoreProperties != null && ignoreProperties.length > 0) {
            boolean flag = false;
            for (String ignoreProperty : ignoreProperties) {
                if (ignoreProperty.equals(name)) {
                    flag = true;
                    break;
                }
            }
            if (flag) {
                return true;
            }
        }
        return false;
    }

    private static <T> T copyField(T source) {
        try {
            Class<?> classType = source.getClass();
            Object objectCopy = source.getClass().getConstructor(new Class[]{}).newInstance();
            //获得对象的所有成员变量
            Field[] fields = classType.getDeclaredFields();
            for (Field field : fields) {
                //获取成员变量的名字
                String name = field.getName();    //获取成员变量的名字，此处为id，name,age
                //跳过静态属性和final属性
                if (skipField(field)) continue;
                ReflectUtil.setAccessible(field);
                field.set(objectCopy, field.get(source));
            }
            return (T) objectCopy;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            throw new CommonException(e);
        }
    }

    private static boolean skipField(Field field) {
        return java.lang.reflect.Modifier.isStatic(field.getModifiers()) || java.lang.reflect.Modifier.isFinal(field.getModifiers());
    }

    private static String[] getNullPropertyNames(Object source, String... ignoreProperties) {
        try {
            Set<String> list = new HashSet<>();
            Class<?> aClass = source.getClass();
            //获取全部的属性
            Field[] declaredFields = aClass.getDeclaredFields();
            //遍历属性
            for (Field declaredField : declaredFields) {
                ReflectUtil.setAccessible(declaredField);
                Object object = declaredField.get(source);
                if (object == null) {
                    list.add(declaredField.getName());
                }
            }
            //添加额外忽略的属性
            list.addAll(Arrays.asList(ignoreProperties));
            return list.toArray(new String[list.size()]);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
